//LPMonitor setup.exe for win2k/xp

#include "stdafx.h"
#include <windows.h>
#include <winspool.h>
#include <tchar.h>
#include <string>
#include <atlbase.h>
#include <iostream>
#include "..\inc\redmon.h"

#pragma warning(disable: 4996)

using namespace std;

//VP SETUP OR UNSTALL return value
#define VP_OK 0                     //Just means ok
#define VP_ERROR -1                 //Some errors we can't expected
#define VP_PRINTERSPOOLER_DISABLED -2   //Printer spooler is diabled
#define VP_PRINTERSPOOLER_RESTARTE_FAILED -3 //Failed to restart printer spooler
#define VP_PORT_FAILED -4       //Failed to add(removed) port
#define VP_DRIVER_FAILED -5     //Failed to add(removed) driver
#define VP_PRINTER_FAILED -6    //Failed to add(removed) printer


typedef BOOL (WINAPI *LPFN_IsWow64Process) (HANDLE, PBOOL);
typedef BOOL (WINAPI *LPFN_Wow64DisableWow64FsRedirection)(PVOID*);
typedef BOOL (WINAPI *LPFN_Wow64RevertWow64FsRedirection)(PVOID);

LPFN_IsWow64Process fnIsWow64Process = 
        (LPFN_IsWow64Process)GetProcAddress(GetModuleHandle(_T("kernel32")), "IsWow64Process");
LPFN_Wow64DisableWow64FsRedirection fnWow64DisableWow64FsRedirection = 
        (LPFN_Wow64DisableWow64FsRedirection)GetProcAddress(GetModuleHandle(_T("kernel32")),"Wow64DisableWow64FsRedirection");
LPFN_Wow64RevertWow64FsRedirection fnWow64RevertWow64FsRedirection = 
        (LPFN_Wow64RevertWow64FsRedirection)GetProcAddress(GetModuleHandle(_T("kernel32")),"Wow64RevertWow64FsRedirection");

BOOL IsWow64()
{
    BOOL bIsWow64 = FALSE;
 
    if (NULL != fnIsWow64Process)
    {
        fnIsWow64Process(GetCurrentProcess(), &bIsWow64);
    }
    return bIsWow64;
}

void DeleteOldPrinter(LPTSTR pPrinterName, LPTSTR pDriverName, LPTSTR pPortName)
{
    HANDLE hPrinter; 
    int err;
    PRINTER_DEFAULTS sPrinterDefault;
    TCHAR Environment[64]	 = {0};
    
    if (IsWow64())
    {
        _tcscpy(Environment, _T("Windows x64"));
    }
    else
    {
        _tcscpy(Environment, _T("Windows NT x86"));
    }

    LPTSTR PrinterName = pPrinterName;
    LPTSTR DriverName  = pDriverName;
    LPTSTR PortName    = pPortName;

    sPrinterDefault.DesiredAccess = PRINTER_ALL_ACCESS;
    sPrinterDefault.pDatatype = NULL;
    sPrinterDefault.pDevMode = NULL;

    if (OpenPrinter(PrinterName, &hPrinter, &sPrinterDefault)) 
    {
        if(!DeletePrinter(hPrinter))
        {
            err = GetLastError();
            cout << "Cant delete printer ,error num = " <<err <<endl;
        }
        ClosePrinter(hPrinter);
    }

    if(!DeletePrinterDriver(NULL, Environment, DriverName))
    {
        err = GetLastError();
        cout << "Cant delete printer driver ,error num = " <<err <<endl;
    }
}

int FindOurPrinter(LPTSTR pPrinterName)
{
    HANDLE hPrinter; 
    PRINTER_DEFAULTS sPrinterDefault;
    TCHAR Environment[64]	 = {0};
    
    if (IsWow64())
    {
        _tcscpy(Environment, _T("Windows x64"));
    }
    else
    {
        _tcscpy(Environment, _T("Windows NT x86"));
    }

    LPTSTR PrinterName = pPrinterName;
    LPTSTR DriverName  = pPrinterName;
    LPTSTR PortName    = pPrinterName;

    sPrinterDefault.DesiredAccess = PRINTER_ALL_ACCESS;
    sPrinterDefault.pDatatype = NULL;
    sPrinterDefault.pDevMode = NULL;

    if (OpenPrinter(PrinterName, &hPrinter, &sPrinterDefault)) 
    {
        ClosePrinter(hPrinter);
        cout<<"Find RapidPrinter Print to File."<<endl;
        return 1;
    }

    cout<<"No find RapidPrinter Print to File."<<endl;

    return 0;
}


BOOL WaitForServiceToReachState(SC_HANDLE hService, DWORD dwDesiredState,
                                SERVICE_STATUS* pss, DWORD dwMilliseconds)
{  
    DWORD dwLastState, dwLastCheckPoint;
    BOOL  fFirstTime = TRUE; // Don't compare state & checkpoint the first time through
    BOOL  fServiceOk = TRUE;
    DWORD dwTimeout = GetTickCount() + dwMilliseconds;
    DWORD dwWaitHintMax = 500;
    DWORD dwWaitHintMin = 100;

    // Loop until the service reaches the desired state,
    // an error occurs, or we timeout
    while  (TRUE)
    {
        // Get current state of service
        fServiceOk = ::QueryServiceStatus(hService, pss);
        cout<<"dwCurrentState="<<pss->dwCurrentState<<",dwCheckPoint="<<pss->dwCheckPoint<<",dwWaitHint="<<pss->dwWaitHint<<endl;

        // If we can't query the service, we're done
        if (!fServiceOk) break;

        // If the service reaches the desired state, we're done
        if (pss->dwCurrentState == dwDesiredState) break;

        // If we timed-out, we're done
        if ((dwMilliseconds != INFINITE) && (dwTimeout < GetTickCount()))
        {
            SetLastError(ERROR_TIMEOUT);
            fServiceOk = FALSE;
            break;
        }

        // If this is our first time, save the service's state & checkpoint
        if (fFirstTime)
        {
            dwLastState = pss->dwCurrentState;
            dwLastCheckPoint = pss->dwCheckPoint;
            fFirstTime = FALSE;
        }
        else
        {
            // If not first time & state has changed, save state & checkpoint
            if (dwLastState != pss->dwCurrentState)
            {
                dwLastState = pss->dwCurrentState;
                dwLastCheckPoint = pss->dwCheckPoint;
            }
            else
            {
                // State hasn't change, check that checkpoint is increasing
                if (pss->dwCheckPoint > dwLastCheckPoint)
                {
                    // Checkpoint has increased, save checkpoint
                    dwLastCheckPoint = pss->dwCheckPoint;
                }
                else
                {
                    // Checkpoint hasn't increased, service failed, we're done!
                    fServiceOk = FALSE; 
                    break;
                }
            }
        }

        // We're not done, wait the specified period of time
        if(pss->dwWaitHint>dwWaitHintMax)
        {
            Sleep(dwWaitHintMax);
        }
        else if(pss->dwWaitHint<dwWaitHintMin)
        {
            Sleep(dwWaitHintMin);
        }
        else
        {
            Sleep(pss->dwWaitHint);
        }
    }

    // Note: The last SERVICE_STATUS is returned to the caller so
    // that the caller can check the service state and error codes.
    return(fServiceOk);
}

int RestartPrinterSpooler()
{
    LPCTSTR pszInternalName = _T("Spooler");

    // Open the SCM and the desired service.
    SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    SC_HANDLE hService = OpenService(hSCM, pszInternalName, SERVICE_STOP | SERVICE_START | SERVICE_QUERY_STATUS);

    // Tell the service to stop.
    SERVICE_STATUS ss;
    BOOL bRet = FALSE;
    int err=0;

    ZeroMemory(&ss, sizeof(ss));

    ControlService(hService, SERVICE_CONTROL_STOP, &ss);

    // Wait up to 15 seconds for the service to stop.
    bRet = WaitForServiceToReachState(hService, SERVICE_STOPPED, &ss, 15000);
    if(FALSE == bRet)
    {
        err = GetLastError();
        cout << "Wait for Service to stop failed,errnum = !"<< err <<endl;
    }

    cout << "Service stop OK!"<<endl; 
    //Tell the service to start
    int iFailedNum = 0;
    do
    {
        cout << "Wait for Service to start"<<endl;
        bRet = StartService(hService, 0, NULL);
        if(FALSE == bRet)
        {
            err = GetLastError();
            if(ERROR_SERVICE_DISABLED == err)
            {
                cout << "Service is disabled!"<<endl;
                break;
            }
            cout << "Start service failed,errnum = !"<< err <<endl;
        }

        // Wait up to 15 seconds for the service to start.
        bRet = WaitForServiceToReachState(hService, SERVICE_RUNNING, &ss, 15000);
        if(FALSE == bRet)
        {
            err = GetLastError();
            cout << "Wait for Service to start failed,errnum = !"<< err <<endl;
        }
        iFailedNum++;
    } while(FALSE == bRet&& 300 > iFailedNum); 

    // Close the service and the SCM.
    CloseServiceHandle(hService);
    CloseServiceHandle(hSCM);
    if(FALSE == bRet)
    {
        if(ERROR_SERVICE_DISABLED == err)
            return VP_PRINTERSPOOLER_DISABLED;
        else
            return VP_PRINTERSPOOLER_RESTARTE_FAILED;
    }
    else
    {
        return VP_OK;
    }
}

void CopyPSFiles(LPCTSTR pDriverDir)
{
	TCHAR pCopyDst[MAX_PATH];
	TCHAR pCopySrc[MAX_PATH];
	TCHAR pCurrentDir[MAX_PATH];
	BOOL bRet;
	int i;

	GetModuleFileName(NULL,pCurrentDir,MAX_PATH);
	(_tcsrchr(pCurrentDir, _T('\\')))[1] = 0;   

	BOOL bIsWow64 = IsWow64();

	TCHAR *sFileNames[] = {
		_T("LongPaperPS.ppd"),
		_T("PS5UI.dll"),
		_T("PSCRIPT5.dll"),
		_T("PSCRIPT.HLP")
	};

	for (i=0; i< 4 ; i++)
	{
		_tcscpy(pCopySrc, pCurrentDir);
		_tcscat(pCopySrc, sFileNames[i]);

		_tcscpy(pCopyDst, (LPCTSTR)pDriverDir);
		_tcscat(pCopyDst, _T("\\"));
		_tcscat(pCopyDst, sFileNames[i]);

		DeleteFile(pCopyDst);
		bRet = CopyFile(pCopySrc, pCopyDst, FALSE);
	}

}

int _tmain(int argc, TCHAR* argv[])
{
    PRINTER_INFO_2 pi2; 
    DRIVER_INFO_3 di3; 
    HANDLE hPrinter; 
    int err;
    ULONG uSize;
    TCHAR Environment[64]	 = {0};

    if (IsWow64())
    {
        _tcscpy(Environment, _T("Windows x64"));
    }
    else
    {
        _tcscpy(Environment, _T("Windows NT x86"));
    }

    LPTSTR PrinterName = _T("FlowerParter Printer");
    LPTSTR DriverName  = _T("FlowerParter Printer Driver");
	LPTSTR PortName    = _T("MPT1:");
	LPTSTR MonitorName = _T("FlowerParter Monitor Port");//MONITORNAME;//

	// below will cause AddPrinter failed because 1796
/*
    //Restart Printer Spooler
    err = RestartPrinterSpooler();
    if(0 > err)
    {
        cout << "RestartPrinterSpooler ret="<< err <<endl;
        return err;
    }
  
    if (argc>1 && _tcsicmp(argv[1], _T("-f")) == 0)
    {
        cout << "Goto find..."<<endl;
        return FindOurPrinter(PrinterName);
    }
*/
    DeleteOldPrinter(PrinterName, DriverName, PortName);

	if (argc>1 && _tcsicmp(argv[1], _T("-u")) == 0)
	{
		cout << "Virtual printer uninstall OK."<<endl;
		return 0;
	}

    //prepare to add printer driver
    TCHAR *pDriverDir = NULL;
  
    GetPrinterDriverDirectory(NULL, Environment, 1, NULL, 0, &uSize);
    if(uSize>0)
    {
        pDriverDir = (TCHAR *)malloc(uSize);
        GetPrinterDriverDirectory(NULL, Environment, 1, (UCHAR*)pDriverDir, uSize, &uSize);
    }
  
    if(NULL == pDriverDir)
    {
        cout << "Cant get printer driver dir of system!" <<endl;
        return VP_DRIVER_FAILED;
    }
  
    TCHAR pDriverPath[MAX_PATH];
    TCHAR pDataFilePath[MAX_PATH];
    TCHAR pConfigFilePath[MAX_PATH];
    TCHAR pHelpFilePath[MAX_PATH];
    TCHAR pDependecy[MAX_PATH] = _T("");
  
    _tcscpy(pDriverPath, (LPCTSTR)pDriverDir);
    _tcscat(pDriverPath, _T("\\PSCRIPT5.DLL"));

    _tcscpy(pDataFilePath, (LPCTSTR)pDriverDir);
    _tcscat(pDataFilePath, _T("\\LongPaperPS.ppd"));

   _tcscpy(pConfigFilePath, (LPCTSTR)pDriverDir);
    _tcscat(pConfigFilePath, _T("\\PS5UI.DLL"));

    _tcscpy(pHelpFilePath, (LPCTSTR)pDriverDir);
    _tcscat(pHelpFilePath, _T("\\PSCRIPT.HLP"));
   
	CopyPSFiles(pDriverDir);
  
    //Add driver
	cout << "Add printer driver..." << endl;
    ZeroMemory(&di3, sizeof(DRIVER_INFO_3));
    di3.cVersion = 3;
    di3.pEnvironment = Environment;
    di3.pName = DriverName;
    di3.pDriverPath = pDriverPath;
    di3.pDataFile = pDataFilePath; 
    di3.pConfigFile = pConfigFilePath;
    di3.pDependentFiles = NULL;
    di3.pHelpFile = pHelpFilePath;
    di3.pMonitorName = NULL;
    di3.pDefaultDataType = _T("RAW");
  
    if(!AddPrinterDriverEx(NULL, 3, (LPBYTE)&di3, APD_COPY_ALL_FILES))
    {
        err = GetLastError();
        cout << "Cant add printer driver,error num = " << err <<endl;
        return VP_DRIVER_FAILED;
    }
  
    //delete file
	TCHAR *sFileNames[] = {
		_T("LongPaperPS.ppd"),
		_T("PS5UI.dll"),
		_T("PSCRIPT5.dll"),
		_T("PSCRIPT.HLP")
	};

	// Delete the file after add printer driver
    TCHAR pCopyDst[MAX_PATH];
	_tcscat(pDriverDir, _T("\\"));

    for(int i=0; i< 3; i++)
    {
        _tcscpy(pCopyDst, (LPCTSTR)pDriverDir);
        _tcscat(pCopyDst, sFileNames[i]);
        
        DeleteFile(pCopyDst);
    }

	cout << "Add printer.." << endl;
    ZeroMemory(&pi2, sizeof(PRINTER_INFO_2)); 
    pi2.pServerName=NULL; 
    pi2.pPrinterName =PrinterName; 
	pi2.pPortName = TEXT("MPT1:");
    pi2.pDriverName = DriverName;
    pi2.pPrintProcessor = _T("WinPrint"); 
    pi2.pDatatype = NULL;
    pi2.Priority = 1;
    hPrinter = AddPrinter(NULL, 2, (LPBYTE)&pi2);
  
    if(NULL == hPrinter)
    {
        err = GetLastError();
        cout << "Cant add printer,error num = " << err <<endl;
        return VP_PRINTER_FAILED;    
    }

    cout << "Virtual printer setup OK."<<endl;

    return VP_OK;
}
